﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ServiceModel;

namespace DotNETX.CommonWcfHost
{
    public class CommonHost : ICommonHostController
    {
        ConfigedServiceReader configSvcReader = new ConfigedServiceReader();
        WcfAssemblyLoader wcfAsmLoader = new WcfAssemblyLoader();
        string[] Folders = null;

        public Dictionary<string, ServiceHost> ServiceHosts { get; protected set; }

        public IEnumerable<string> ServiceNames { get { return configSvcReader == null ? null : configSvcReader.Names; } }

        public IEnumerable<string> GetServiceNames()
        {
            return ServiceNames;
        }

        protected Type TryFindType(string typeName)
        {
            Type type = null;
            if (null != wcfAsmLoader.LoadedAssemblys)
                foreach (Assembly asm in wcfAsmLoader.LoadedAssemblys)
                {
                    type = asm.GetType(typeName, false);
                    if (type != null)
                    {
                        return type;
                    }
                }
            type = Type.GetType(typeName);
            if (type != null) return type;
            type = Assembly.GetCallingAssembly().GetType(typeName, false);
            if (type == null) type = Assembly.GetEntryAssembly().GetType(typeName, false);
            if (type != null) return type;

            foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = asm.GetType(typeName, false);
                if (type != null)
                {
                    return type;
                }
            }
            return null;
        }

        protected ServiceHost CreatServiceHost(string name)
        {
            Type type = TryFindType(name);
            if (type == null || !type.IsClass || type.IsAbstract) return null;
            else
            {
                try
                {
                    ServiceHost host = new ServiceHost(type);

                    host.Closed += (s, e) => { if (Closed != null)Closed(s, new ServiceHostEventArgs() { Name = name }); };
                    host.Closing += (s, e) => { if (Closing != null)Closing(s, new ServiceHostEventArgs() { Name = name }); };
                    host.Faulted += (s, e) => { if (Faulted != null)Faulted(s, new ServiceHostEventArgs() { Name = name }); };
                    host.Opened += (s, e) => { if (Opened != null)Opened(s, new ServiceHostEventArgs() { Name = name }); };
                    host.Opening += (s, e) => { if (Opening != null)Opening(s, new ServiceHostEventArgs() { Name = name }); };
                    return host;
                }
                catch
                {
                    return null;
                }
            }
        }

        protected void InitialServiceHosts(IEnumerable<string> names)
        {
            if (null == ServiceHosts)
            {
                ServiceHosts = new Dictionary<string, ServiceHost>();
            }
            if (null != names)
                foreach (string name in names)
                {
                    if (ServiceHosts.ContainsKey(name)) continue;
                    ServiceHost host = CreatServiceHost(name);
                    if (host != null)
                    {
                        ServiceHosts.Add(name, host);
                    }
                }
        }

        public void SetServiceAssemblyFolders(string[] folders)
        {
            Folders = folders;
        }

        public void Initial()
        {
            configSvcReader.Read();
            if (null != Folders)
            {
                DotNETX.Utility.ProgramUtils.CHANGE_PRIVATE_BINPATH(string.Join(";", Folders)+";");
                foreach (string folder in Folders)
                {
                    if (!string.IsNullOrEmpty(folder))
                        wcfAsmLoader.LoadAssemblyInFolder(folder.Trim());
                }
            }
                
            InitialServiceHosts(configSvcReader.Names);
        }

        public bool Open(string name)
        {
            ServiceHost host = ServiceHosts.ContainsKey(name) ? ServiceHosts[name] : null;
            if (null == host) return false;
            if (host.State == CommunicationState.Closed
                || host.State == CommunicationState.Closing
                || host.State == CommunicationState.Faulted)
            {
                lock (ServiceHosts)
                {
                    host = ServiceHosts[name] = CreatServiceHost(name);
                }
            }
            if (host.State != CommunicationState.Opened || host.State != CommunicationState.Opening)
            {
                try
                {
                    host.Open();
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            else return true;
        }

        public bool ReOpen(string name)
        {
            Close(name);
            return Open(name);
        }

        public CommunicationState GetStatus(string name)
        {
            ServiceHost host = ServiceHosts.ContainsKey(name) ? ServiceHosts[name] : null;
            if (null == host) return CommunicationState.Faulted;
            return host.State;
        }

        public bool Close(string name)
        {
            ServiceHost host = ServiceHosts.ContainsKey(name) ? ServiceHosts[name] : null;
            if (null == host) return false;

            if (host.State != CommunicationState.Opening)
            {
                try
                {
                    host.Abort();
                    host.Close();
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            return false;
        }

        public int OpenAll()
        {
            int count = 0;
            foreach (string name in ServiceNames)
            {
                if (Open(name)) count++;
            }
            return count;
        }
        public int CloseAll()
        {
            int count = 0;
            foreach (string name in ServiceNames)
            {
                if (Close(name)) count++;
            }
            return count;
        }

        public int CountOpened()
        {
            return ServiceHosts.Values.Count((sh) => { return sh != null && sh.State == CommunicationState.Opened; });
        }
        public int CountNotOpened()
        {
            return ServiceHosts.Values.Count((sh) => { return sh == null || sh.State != CommunicationState.Opened; });
        }

        //
        // 摘要:
        //     当通信对象转换到已关闭状态时发生。
        public event EventHandler<ServiceHostEventArgs> Closed;
        //
        // 摘要:
        //     当通信对象转换到正在关闭状态时发生。
        public event EventHandler<ServiceHostEventArgs> Closing;
        //
        // 摘要:
        //     在通信对象转换到出错状态时发生。
        public event EventHandler<ServiceHostEventArgs> Faulted;
        //
        // 摘要:
        //     当通信对象转换到已打开状态时发生。
        public event EventHandler<ServiceHostEventArgs> Opened;
        //
        // 摘要:
        //     当通信对象转换到正在打开状态时发生。
        public event EventHandler<ServiceHostEventArgs> Opening;
    }
}
